package org.manon.json.analyse;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import org.manon.json.JSONException;
import org.manon.json.meta.Entity;
import org.manon.json.meta.HasUniqueId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author <a href="mailto:jacques.militello@gmail.com">Jacques Militello</a>
 */
public final class TreeAnalyser {

	private final Logger _logger = LoggerFactory.getLogger(TreeAnalyser.class);
	
	private Map<Class<?>,Entity> _entities;
	
	public TreeAnalyser(Map<Class<?>,Entity> entities) {
		_entities = entities;
	}
	
	/**
	 * Analyze Object tree.
	 */
	public final Tree<Entity,String> buildTree() {
		
		final Tree<Entity,String> tree = new Tree<Entity,String>(new Comparator<HasUniqueId<String>>() {
			public int compare(HasUniqueId<String> o1, HasUniqueId<String> o2) {
				return o1.getId().compareTo(o2.getId());
			}
		});
		
		for (Entity entity : _entities.values()) {
			String[] path = buildTreePath(entity,_entities);
			if (tree.contains(path)) {
				if (_logger.isDebugEnabled()) {
					_logger.debug("The Entity [{}] is already present in the tree.");
				}
				continue;
			}
			add(tree,tree.newTreeNode(entity));
		}

		return tree;
	}
	
	
	static String[] buildTreePath(Entity root,Map<Class<?>,Entity> entities) {
		List<String> path = new ArrayList<String>();
		buildTreePath(path,root,entities);
		return path.toArray(new String[path.size()]);
	}
	
	static void buildTreePath(List<String> path,Entity entity,Map<Class<?>,Entity>  entities) {
		path.add(0, entity.getId());
		if (Object.class.equals(entity.getEntityClass().getSuperclass())) {
			return;
		} else {
			Entity entiyParent = entities.get(entity.getEntityClass().getSuperclass());
			
			if (entiyParent == null) {
				throw new JSONException("The Entity [" + entity + "] has a SuperClass [" + entity.getEntityClass().getSuperclass() +"] that is not registered to JSONFactory");
			}
			
			buildTreePath(path,entiyParent,entities);
		}
		
	}
	
	private void add(final Tree<Entity,String> tree,TreeNode<Entity,String> node) {
		
		if (Object.class.equals(node.getObject().getEntityClass().getSuperclass())) {
			
			if (_logger.isDebugEnabled()) {
				_logger.debug("add root node [{}]",node.getId());
			}
			
			tree.addRoot(node);
			return;
		}
		
		Entity entiyParent = _entities.get(node.getObject().getEntityClass().getSuperclass());
		
		if (entiyParent == null) {
			throw new JSONException("Configuration Problems !!!");
		}
		
		TreeNode<Entity,String> parentNode = tree.search(buildTreePath(entiyParent,_entities));
		
		if (parentNode == null) {
			add(tree,tree.newTreeNode(_entities.get(node.getObject().getEntityClass().getSuperclass())));	
			add(tree,node);
		} else {
			parentNode.add(node);
		}
	}
}